﻿// <copyright file="KingManager.cs" company="Ecole Normale Supérieure">
// Released under GPLv2
// </copyright>
// <summary>Contains the class KingManager that realize the interface IKingMAnager which provides operation of kings.</summary>

namespace Sqwarea.GameLogic
{
    using System;
    using System.Drawing;
    using Sqwarea.GameLogic.Extensions;
    using Sqwarea.Persistence;
    using Sqwarea.Persistence.Exceptions;

    /// <summary>
    /// This class provides management operation for a king
    /// King or KingId must be set before any use of the class.
    /// </summary>
    public class KingManager : IKingManager
    {
        /// <summary>
        /// The game provider used to gather information
        /// </summary>
        private IGameProvider _gameProvider;

        /// <summary>
        /// The king that performs operations
        /// </summary>
        private King _king;

        /// <summary>
        /// The king id
        /// </summary>
        private long _kingId;

        /// <summary>
        /// Initializes a new instance of the KingManager class with a GameProvider.
        /// WARNING : Before using this class please set King or KingId property.
        /// </summary>
        /// <param name="gameProvider">GameProvider for gathering game information</param>
        public KingManager(IGameProvider gameProvider)
        {
            if (gameProvider == null)
            {
                throw new ArgumentNullException("gameProvider", "The GameProvider provided is null.");
            }

            _gameProvider = gameProvider;
        }

        /// <summary>
        /// Gets or sets the id of the used king.
        /// </summary>
        public long KingId
        {
            get
            {
                return _kingId;
            }

            set
            {
                _kingId = value;
                try
                {
                    _king = _gameProvider.GetKingById(_kingId);
                }
                catch (PersistenceKingNotFoundException)
                {
                    _king = null;
                    _kingId = -1;
                    throw;
                }

                if (_king == null)
                {
                    _kingId = -1;
                    throw new InvalidOperationException("King is null although there was no PersistenceKingNotFoundException.");
                }
            }
        }

        /// <summary>
        /// Gets or sets the used king.
        /// </summary>
        public King King
        {
            get
            {
                return _king;
            }

            set
            {
                _king = value;
                if (_king == null)
                {
                    _kingId = -1;
                    throw new ArgumentException("The kingid provided do not correspond to a king.");
                }

                _kingId = _king.Id;
            }
        }

        /// <summary>
        /// Perform movement of the king
        /// </summary>
        /// <param name="direction">Direction to which the king moves</param>
        /// <returns>Whether the movement is successful</returns>
        public bool MoveKing(Direction direction)
        {
            if (this._king.LastKingMoveDate.AddHours(GlobalParameters.MinimumHoursBetweenTwoKingMove).CompareTo(DateTimeOffset.Now) > 0)
            {
                return false;
            }
            else
            {
                Square kingsquare = _gameProvider.GetSquareByPosition(_king.Position);
                Point newpos = _king.Position.GetDir(direction);
                Square mysquare = _gameProvider.GetSquareByPosition(newpos);
                if (mysquare.KingId != _king.Id)
                {
                    return false;
                }

                mysquare.HasKing = true;
                kingsquare.HasKing = false;
                kingsquare.PathToKing = direction;

                bool success = true;
                _king = _gameProvider.UpdateKing(
                    _king.Id,
                    k =>
                        {
                            // Hack to have a normal behaviour with Lokad's mock
                            // var k1 = (King) k.Clone();
                            var k1 = k;
                            if (
                                k1.LastKingMoveDate.AddHours(GlobalParameters.MinimumHoursBetweenTwoKingMove).CompareTo(
                                    DateTimeOffset.Now) > 0)
                            {
                                success = false;
                                return k;
                            }

                            k1.Position = newpos;
                            k1.LastKingMoveDate = DateTimeOffset.Now;
                            return k1;
                        });

                if (!success)
                {
                    return false;
                }

                _gameProvider.UpdateSquare(
                    kingsquare.Position,
                    s =>
                    {
                        var s1 = s;
                        s1.HasKing = false;
                        s1.PathToKing = direction;
                        return s1;
                    });
                _gameProvider.UpdateSquare(
                    mysquare.Position,
                    s =>
                    {
                        var s1 = s;
                        s1.HasKing = true;
                        return s1;
                    });
                return true;
            }
        }

        /// <summary>
        /// Perform an attack from the king
        /// </summary>
        /// <param name="destination">Destination of this attack</param>
        /// <param name="troops">Number of troops to send</param>
        /// <returns>If the operation is successful</returns>
        public bool SendTroops(Point destination, int troops)
        {
            if ((troops <= 0) || (this._king.GetAcademyTroops() < troops))
            {
                return false;
            }
            else
            {
                bool success = true;
                this._king = this._gameProvider.UpdateKing(
                    this._king.Id,
                    k =>
                        {
                            // Hack to have a normal behaviour with Lokad's mock
                            // var k1 = (King) k.Clone();
                            var k1 = k;
                            if (k1.GetAcademyTroops() < troops)
                            {
                                success = false;
                                return k;
                            }

                            k1.AcademyTroops -= troops;
                            return k1;
                        });

                if (!success)
                {
                    return false;
                }

                var newattack = new Attack
                       {
                           Destination = destination,
                           TroopsNumber = troops,
                           ArrivalDate = DateTimeOffset.Now.AddMinutes(GlobalParameters.SoldiersSpeed * this._king.Position.GetDistanceTo(destination)),
                           SenderId = this._king.Id
                       };
                this._gameProvider.NewAttack(newattack);

                return true;
            }
        }

        /// <summary>
        /// Take number troops on the square of the king on the academy
        /// </summary>
        /// <param name="number">the number of troops</param>
        /// <returns>If the operation is successful</returns>
        public bool TakeTroops(int number)
        {
            King.UpdateAcademyTroops();
            var square = _gameProvider.GetSquareByPosition(King.Position);
            if (square.Troops < number)
            {
                return false;
            }

            if (King.AcademyTroops + number > King.MaxAcademyTroops())
            {
                return false;
            }

            bool success = true;
            _gameProvider.UpdateSquare(
                        square.Position,
                        s =>
                        {
                            var s1 = s;
                            if (s1.Troops < number)
                            {
                                success = false;
                                return null;
                            }

                            s1.Troops -= number;
                            return s1;
                        });
            if (!success)
            {
                return false;
            }

            _king = _gameProvider.UpdateKing(
                _king.Id,
                k =>
                    {
                        // Hack to have a normal behaviour with Lokad's mock
                        // var k1 = (King)k.Clone();
                        var k1 = k;
                        
                        if (k1.GetAcademyTroops() + number > k1.MaxAcademyTroops())
                        {
                            success = false;
                            return k;
                        }

                        k1.AcademyTroops += number;
                        return k1;
                    });

            if (!success)
            {
                _gameProvider.UpdateSquare(
                            square.Position,
                            s =>
                            {
                                var s1 = s;
                                s1.Troops += number;
                                return s1;
                            });

                return false;
            }

            return true;
        }
    }
}
